/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { SharedValue, useAnimatedGestureHandler } from 'react-native-reanimated';
import { State, PanGestureHandlerGestureEvent } from 'react-native-gesture-handler';
import { GESTURE_SOURCE } from '../constants';
import type { GestureEventContextType, GestureEventHandlerCallbackType } from '../types';

const resetContext = (context: GestureEventContextType): void => {
  'worklet';
  Object.keys(context).map(key => {
    context[key as keyof GestureEventContextType] = undefined;
  });
};

const createOnActiveHandler = (
  type: GESTURE_SOURCE,
  state: SharedValue<State>,
  gestureSource: SharedValue<GESTURE_SOURCE>,
  handleOnStart: GestureEventHandlerCallbackType,
  handleOnActive: GestureEventHandlerCallbackType,
): ((payload: PanGestureHandlerGestureEvent['nativeEvent'], context: GestureEventContextType) => void) => {
  return (payload: PanGestureHandlerGestureEvent['nativeEvent'], context: GestureEventContextType): void => {
    'worklet';
    if (!context.didStart) {
      context.didStart = true;
      state.value = State.BEGAN;
      gestureSource.value = type;
      handleOnStart(type, payload, context);
      return;
    }

    if (gestureSource.value !== type) {
      return;
    }

    state.value = payload.state;
    handleOnActive(type, payload, context);
  };
};

const createOnEndHandler = (
  type: GESTURE_SOURCE,
  state: SharedValue<State>,
  gestureSource: SharedValue<GESTURE_SOURCE>,
  handleOnEnd: GestureEventHandlerCallbackType,
): ((payload: PanGestureHandlerGestureEvent['nativeEvent'], context: GestureEventContextType) => void) => {
  return (payload: PanGestureHandlerGestureEvent['nativeEvent'], context: GestureEventContextType): void => {
    'worklet';
    if (gestureSource.value !== type) {
      return;
    }

    state.value = payload.state;
    gestureSource.value = GESTURE_SOURCE.UNDETERMINED;
    handleOnEnd(type, payload, context);
    resetContext(context);
  };
};

const createTerminationHandler = (
  type: GESTURE_SOURCE,
  state: SharedValue<State>,
  gestureSource: SharedValue<GESTURE_SOURCE>,
): ((payload: PanGestureHandlerGestureEvent['nativeEvent'], context: GestureEventContextType) => void) => {
  return (payload: PanGestureHandlerGestureEvent['nativeEvent'], context: GestureEventContextType): void => {
    'worklet';
    if (gestureSource.value !== type) {
      return;
    }

    state.value = payload.state;
    gestureSource.value = GESTURE_SOURCE.UNDETERMINED;
    resetContext(context);
  };
};

export const useGestureHandler = (
  type: GESTURE_SOURCE,
  state: SharedValue<State>,
  gestureSource: SharedValue<GESTURE_SOURCE>,
  handleOnStart: GestureEventHandlerCallbackType,
  handleOnActive: GestureEventHandlerCallbackType,
  handleOnEnd: GestureEventHandlerCallbackType,
): ((event: PanGestureHandlerGestureEvent) => void) => {
  const onActive = createOnActiveHandler(type, state, gestureSource, handleOnStart, handleOnActive);
  const onEnd = createOnEndHandler(type, state, gestureSource, handleOnEnd);
  const onTermination = createTerminationHandler(type, state, gestureSource);

  const gestureHandler = useAnimatedGestureHandler<PanGestureHandlerGestureEvent, GestureEventContextType>(
    {
      onActive,
      onEnd,
      onCancel: onTermination,
      onFail: onTermination,
      onFinish: onTermination,
    },
    [type, state, handleOnStart, handleOnActive, handleOnEnd],
  );

  return gestureHandler;
};
